package com.example.lotterysystem.service.impl;


import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ControllerException;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.ActivityPrize;
import com.example.lotterysystem.controller.param.ActivityUser;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.PageParam;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.IActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 功能描述:
 *
 * @author Lenovo
 * @date 2025/1/31
 */
@Slf4j
@Service
public class IActivityServiceImpl implements IActivityService {
    /**
     * 活动缓存前缀
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";

    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(Integer createId,CreateActivityParam param) {
        //校验参数
        checkActivityInfo(param);
        //存储活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.create(createId,activityDO);
        //存储活动关联的奖品信息
        List<ActivityPrize> activityPrizeList = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeList
                .stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //存储活动关联的人员信息
        List<ActivityUser> activityUserList = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = activityUserList
                .stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //获取奖品基本属性列表
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(ActivityPrize::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合信息，存储到Redis中
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO,
                activityUserDOList, activityPrizeDOList, prizeDOList);

        cacheActivity(detailDTO);
        //构造返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> selectActivityByPage(Integer id, PageParam param) {
        //获取总量
        int total = activityMapper.count();
        //获取列表
        List<ActivityDO> activityDOList = activityMapper
                .selectActivityByPage(id, param.offset(), param.getPageSize());
        List<ActivityDTO> result = activityDOList
                .stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, result);
    }

    @Override
    public ActivityDetailDTO selectActivityDetail(Long activityId) {
        if (activityId == null) {
            return null;
        }
        //先查询Redis
        ActivityDetailDTO activityFromCache = getActivityFromCache(activityId);
        if (activityFromCache != null) {
            log.info("Redis中获取到活动详细信息:{}", JacksonUtil.writeValueAsString(activityFromCache));
            return activityFromCache;
        }
        //如果Redis不存在，查询数据库
        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);
        //活动奖品表
        List<ActivityPrizeDO> aPDOList = activityPrizeMapper.selectPrizeByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> uDOList = activityUserMapper.selectUserByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = aPDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合
        ActivityDetailDTO result = convertToActivityDetailDTO(aDO, uDOList, aPDOList, pDOList);
        //存放到Redis
        cacheActivity(result);
        return result;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (activityId == null) {
            log.warn("要缓存的 activityId 为 null");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //查询表数据
        //活动表
        ActivityDO aDO = activityMapper.selectActivityById(activityId);
        if (aDO == null) {
            log.error("要缓存的 activityId 有误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        //活动奖品表
        List<ActivityPrizeDO> aPDOList = activityPrizeMapper.selectPrizeByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> uDOList = activityUserMapper.selectUserByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = aPDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合
        ActivityDetailDTO result = convertToActivityDetailDTO(aDO, uDOList, aPDOList, pDOList);
        //存放到Redis
        cacheActivity(result);
    }

    /**
     * 缓存完整的活动信息
     *
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        if (detailDTO == null || detailDTO.getActivityId() == null) {
            log.warn("缓存的活动信息不存在");
            return;
        }
        //不对异常进行抛出，缓存失败也不需要回滚事务
        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO), ACTIVITY_TIMEOUT);
        } catch (Exception e) {
            log.error("缓存活动失败：ActivityDetailDTO = {}",
                    JacksonUtil.writeValueAsString(detailDTO), e);
        }
    }

    /**
     * 根据活动id从缓存中获取活动信息
     *
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if (activityId == null) {
            log.warn("获取缓存活动信息 activityId 为空");
            return null;
        }

        String result = null;
        try {
            result = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if (!StringUtils.hasText(result)) {
                log.info("获取缓存活动信息为空，key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(result, ActivityDetailDTO.class);
        } catch (Exception e) {
            log.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId);
            return null;
        }

    }

    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<ActivityPrizeDO> activityPrizeList,
                                                         List<PrizeDO> prizeDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        //转化活动列表类型
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeList.stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    //相当于联表查询
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream().filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        detailDTO.setPrizeDTOList(prizeDTOList);

        //转换活动用户列表类型
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream().map(activityUserDO -> {
            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
            userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
            userDTO.setUserId(activityUserDO.getUserId());
            userDTO.setUserName(activityUserDO.getUserName());
            return userDTO;
        }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);


        return detailDTO;
    }

    /**
     * 校验创建活动参数
     *
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (param == null) {
            throw new ControllerException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //校验人员是否存在
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(ActivityUser::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectUserInfoById(userIds);
        if (existUserIds == null) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //校验奖品是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(ActivityPrize::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);

        if (CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });
        //校验奖品数量（人员数量 >= 奖品数量）
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(ActivityPrize::getPrizeAmount)
                .sum();
        if (userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }
        //校验活动奖品等级有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (ActivityPrizeTiersEnum.forName(prize.getPrizeTiers()) == null) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
